using System;
using System.Collections.Generic;
using System.Configuration;
using System.Xml;
using Common.Unity.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity.Configuration;
using Common.Unity.Extensions;


namespace Common.Unity.Extensions.Configuration.Configuration
{
	public abstract class ObjectConfigurationElement : UnityContainerTypeConfigurationElement
	{
		[ConfigurationCollection( typeof(DependencyReferenceConfigurationElement),
			CollectionType = ConfigurationElementCollectionType.AddRemoveClearMap, AddItemName = "objectConfigurator" ),
		 ConfigurationProperty( "", IsDefaultCollection = true )]
		public TypeResolvingConfigurationElementCollection<DependencyReferenceConfigurationElement> Configurators
		{
			get
			{
				TypeResolvingConfigurationElementCollection<DependencyReferenceConfigurationElement> result =
					(TypeResolvingConfigurationElementCollection<DependencyReferenceConfigurationElement>)this[string.Empty];
				result.TypeResolver = TypeResolver;
				return result;
			}
		}

		public override void Configure(Microsoft.Practices.Unity.IUnityContainer container)
		{
			IConfigurationExtensionConfiguration configuration = container.Configure<IConfigurationExtensionConfiguration>();
			if ( configuration != null )
			{
				// Register any object configurations:
				string buildKeyName = !string.IsNullOrEmpty( ParentElement.Name ) ? ParentElement.Name : null;
				foreach ( Type type in GetObjectConfigurations() )
				{
					if ( !container.IsRegistered( type, buildKeyName ) )
					{
						container.RegisterInstance( type, buildKeyName, this );
					}
				}

				// Setup any configurator policies:
				IObjectConfiguratorPolicy policy = BuilderPolicyHelper.EnsurePolicy<IObjectConfiguratorPolicy, ObjectConfiguratorPolicy>( configuration.Policies, new NamedTypeBuildKey( ParentElement.MapTo ?? ParentElement.Type, ParentElement.Name ) );
				foreach ( DependencyReferenceConfigurationElement element in Configurators )
				{
					IObjectConfigurator configurator =
						(IObjectConfigurator)container.Resolve( TypeResolver.ResolveType( element.TypeName ), element.Name ?? buildKeyName );
					policy.Configurators.Add( configurator );
				}
			}

			base.Configure(container);
		}
		IEnumerable<Type> GetObjectConfigurations()
		{
			foreach ( Type type in ResolveObjectConfigurations() )
			{
				if ( type != typeof(IObjectConfiguration) && typeof(IObjectConfiguration).IsAssignableFrom( type ) )
				{
					yield return type;
				}
			}
		}

		/// <summary>
		/// Used to resolve the IObjectConfigurations that this object provides.
		/// </summary>
		/// <returns></returns>
		protected virtual IEnumerable<Type> ResolveObjectConfigurations()
		{
			return GetType().GetInterfaces();
		}
	}
}